/*
 * Line.java
 * 
 * Created by demory on Mar 30, 2009, 8:54:35 PM
 * 
 * Copyright 2008 David D. Emory
 * 
 * This file is part of Five Points. See <http://www.fpdev.org> for
 * additional information regarding the project.
 * 
 * Five Points is free software: you can redistribute it and/or modify
 * it under the terms of the GNU General Public License as published by
 * the Free Software Foundation, either version 3 of the License, or
 * (at your option) any later version.
 * 
 * Five Points is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU General Public License for more details.
 * 
 * You should have received a copy of the GNU General Public License
 * along with Five Points.  If not, see <http://www.gnu.org/licenses/>.
 */

package org.fpdev.apps.cart.network;

import java.awt.BasicStroke;
import java.awt.Color;
import java.awt.Graphics2D;
import java.awt.geom.Path2D;
import java.util.ArrayList;
import java.util.Calendar;
import java.util.Collection;
import java.util.Date;
import java.util.HashMap;
import java.util.List;
import java.util.ListIterator;
import java.util.Map;
import org.fpdev.util.gui.MapCoordinates;
import org.fpdev.apps.cart.gui.map.Canvas;

/**
 *
 * @author demory
 */
public class Line implements Comparable {

  private int id_;
  private int openMonth_, openYear_;

  private LineStyle style_;
  private String name_;

  private List<Corridor> corridors_;
  private Map<Integer, CorridorInfo> corridorInfo_;

  private boolean enabled_, highlighted_;


  public Line(int id, String name) {//, Color color, int width, int offset) {
    this(id, name, new LineStyle(LineStyle.Preset.DEFAULT));
  }
  
  public Line(int id, String name, LineStyle style) {
    id_ = id;
    name_ = name;
    corridors_ = new ArrayList<Corridor>();
    corridorInfo_ = new HashMap<Integer, CorridorInfo>();
    style_ = style;
    enabled_ = true;
    highlighted_ = false;
  }


  public int getID() {
    return id_;
  }
  
  public String getName() {
    return name_;
  }

  public boolean isEnabled() {
    return enabled_;
  }

  public void setEnabled(boolean enabled) {
    enabled_ = enabled;
  }

  public int getOpenMonth() {
    return openMonth_;
  }

  public void setOpenMonth(int openMonth) {
    openMonth_ = openMonth;
  }

  public int getOpenYear() {
    return openYear_;
  }

  public void setOpenYear(int openYear) {
    openYear_ = openYear;
  }

  public Date getOpenDate() {
    Calendar cal = Calendar.getInstance();
    if(openYear_ == 0) {
      cal.set(1, 1, 1);
      return cal.getTime();
    }
    cal.set(openYear_, openMonth_-1, 1);
    return cal.getTime();
  }

  public LineStyle getStyle() {
    return style_;
  }

  public void setStyle(LineStyle style) {
    style_ = style;
  }

  public AnchorPoint startPoint() {
    if(corridors_.size() == 0) return null;
    if(corridors_.size() == 1) return corridors_.get(0).fPoint();

    Corridor first = corridors_.get(0), second = corridors_.get(1);
    if(first.tPoint() == second.fPoint() || first.tPoint() == second.tPoint())
      return first.fPoint();
    if(first.fPoint() == second.fPoint() || first.fPoint() == second.tPoint())
      return first.tPoint();

    return null;
  }

  public AnchorPoint endPoint() {
    if(corridors_.size() == 0) return null;
    if(corridors_.size() == 1) return corridors_.get(0).tPoint();

    Corridor last = corridors_.get(corridors_.size()-1), previous = corridors_.get(corridors_.size()-2);
    if(last.tPoint() == previous.fPoint() || last.tPoint() == previous.tPoint())
      return last.fPoint();
    if(last.fPoint() == previous.fPoint() || last.fPoint() == previous.tPoint())
      return last.tPoint();

    return null;
  }

  public Corridor firstCorridor() {
    if(corridors_.size() == 0) return null;
    return corridors_.get(0);
  }

  public Corridor lastCorridor() {
    if(corridors_.size() == 0) return null;
    return corridors_.get(corridors_.size()-1);
  }

  public AnchorPoint fromPoint(Corridor corr) {
    AnchorPoint from = startPoint(), to;
    for(Corridor c : corridors_) {
      to = c.opposite(from);
      if(c == corr) return from;
      from = to;
    }
    return null;
  }

  public AnchorPoint toPoint(Corridor corr) {
    AnchorPoint from = startPoint(), to;
    for(Corridor c : corridors_) {
      to = c.opposite(from);
      if(c == corr) return to;
      from = to;
    }
    return null;
  }

  public Corridor adjacent(Corridor corr, AnchorPoint anchor) {

    if(corr.fPoint() != anchor && corr.tPoint() != anchor) return null;
    if(anchor == startPoint() || anchor == endPoint()) return null;


    AnchorPoint from = startPoint(), to;
    int i = 0;
    for(Corridor c : corridors_) {
      to = c.opposite(from);
      if(c == corr) {
        if(from == anchor) return corridors_.get(i-1);
        if(to == anchor) return corridors_.get(i+1);
      }
      from = to;
      i++;
    }

    return null;
  }


  public int compareTo(Object o) {
    return name_.compareTo(((Line) o).getName());
  }

  public void initCorridor(Corridor corr) {
    initCorridor(corr, 0, 0, 0, 0);
  }
  
  public void initCorridor(Corridor corr, int offsetFrom, int offsetTo, int openMonth, int openYear) {
    corridors_.add(corr);
    corridorInfo_.put(corr.getID(), new CorridorInfo(offsetFrom, offsetTo, openMonth, openYear));
    corr.registerLine(this);
  }

  public boolean contains(Corridor c) {
    return corridors_.contains(c);
  }

  public boolean contains(Line l) {
    for(Corridor c : l.corridors_)
      if(!this.contains(c)) return false;
    return true;
  }

  /**
   *
   * @param line
   * @return 1 if in order, -1 if in reverse order, 0 if not contained in order
   */
  public int containsInOrder(Line line) {
    String thisStr = "", fwStr = "", bwStr = "";
    for(Corridor corr : corridors_) {
      thisStr += corr.getID() + "_";
    }
    for(Corridor corr : line.corridors_) {
      fwStr += corr.getID() + "_";
      bwStr = corr.getID() + "_" + bwStr;
    }

    if(thisStr.contains(fwStr)) return 1;
    if(thisStr.contains(bwStr)) return -1;
    return 0;
  }

  public void initBaseOffset(int offset) {
    AnchorPoint a = startPoint();
    for(Corridor c : corridors_) {
      CorridorInfo ci = corridorInfo_.get(c.getID());
      if(a != c.fPoint()) {
        ci.offsetFrom_ = -offset;
        ci.offsetTo_ = -offset;
        a = c.fPoint();
      }
      else {
        ci.offsetFrom_ = offset;
        ci.offsetTo_ = offset;
        a = c.tPoint();
      }
    }
  }

  public boolean addCorridor(Corridor corr) {
    if(corridors_.isEmpty()) {
      corridors_.add(corr);
      corridorInfo_.put(corr.getID(), new CorridorInfo(0, 0, 0, 0));
      corr.registerLine(this);
      return true;
    }
    if(corr.adjacentTo(corridors_.get(0))) {
      corridors_.add(0, corr);
      corridorInfo_.put(corr.getID(), new CorridorInfo(0, 0, 0, 0));
      corr.registerLine(this);
      return true;
    }
    if(corr.adjacentTo(corridors_.get(corridors_.size()-1))) {
      corridors_.add(corridors_.size(), corr);
      corridorInfo_.put(corr.getID(), new CorridorInfo(0, 0, 0, 0));
      corr.registerLine(this);
      return true;
    }
    return false;
  }

  public boolean removeCorridor(Corridor corr) {
    boolean success = corridors_.remove(corr);
    if(success) {
      corridorInfo_.remove(corr.getID());
      corr.unregisterLine(this);
    }
    return success;
  }

  public void clearOffsets() {
    for(CorridorInfo ci : corridorInfo_.values())
      ci.offsetFrom_ = ci.offsetTo_ = 0;
  }

  public Collection<Corridor> getCorridors() {
    return corridors_;
  }

  public CorridorInfo getCorridorInfo(Corridor corr) {
    return corridorInfo_.get(corr.getID());
  }

  public void setHighlighted(boolean highlighted) {
    highlighted_ = highlighted;
  }
  
  public void draw(Canvas canvas) {
    draw(canvas.getGraphics2D(), canvas.getCoordinates());
  }

  public void draw(Graphics2D g2d, MapCoordinates coords) {
    AnchorPoint a = startPoint();
    
    if(highlighted_) {
      g2d.setColor(Color.YELLOW);
      if(a != null)
        g2d.drawOval(coords.xToScreen(a.getX())-5, coords.yToScreen(a.getY())-4, 8, 8);
    }

    /*for(Corridor c : corridors_) {


      CorridorInfo ci = corridorInfo_.get(c.getID());
      int offsetFrom = ci.offsetFrom_;
      int offsetTo = ci.offsetTo_;

      if(highlighted_) c.draw(g2d, coords, offsetFrom, offsetTo, style_.getHighlight());
      ListIterator<SubStyle> iter = style_.getSubStyles().listIterator(style_.getSubStyles().size());
      while(iter.hasPrevious())
        c.draw(g2d, coords, offsetFrom, offsetTo, iter.previous());
    }*/

    //List<Point2D> points = new LinkedList<Point2D>();
    Path2D path = new Path2D.Double();
    AnchorPoint fromPt = this.startPoint();
    //path.moveTo(fromPt.getX(), fromPt.getY());
    for(Corridor c : corridors_) {
      CorridorInfo ci = corridorInfo_.get(c.getID());
      int offsetFrom = ci.offsetFrom_;
      int offsetTo = ci.offsetTo_;
      path.append(c.getPath(fromPt, offsetFrom, offsetTo, coords), true);
      fromPt = c.opposite(fromPt);
    }

    ListIterator<SubStyle> iter = style_.getSubStyles().listIterator(style_.getSubStyles().size());
    while(iter.hasPrevious()) {
      SubStyle sstyle = iter.previous();
      g2d.setColor(sstyle.getColor());
      g2d.setStroke(sstyle.getStroke());
      g2d.draw(path);
    }


  }

  public String getXML(String prefix) {
    String corrIDs = "";
    for(Corridor corr : corridors_) {
      corrIDs += corr.getID()+",";
    }
    if(corrIDs.length() > 0) corrIDs = corrIDs.substring(0, corrIDs.length()-1); // chop off trailing comma


    String opendate = (openMonth_ < 10 ? "0" : "") + openMonth_ + openYear_;
    return prefix + "<line id=\""+id_+"\" name=\""+name_+"\" style=\""+style_.getID()+"\">"+corrIDs+"</line>\n"; //color=\""+style_.getColor().getRGB()+"\" width=\""+style_.getWidth()+"\" offset=\""+style_.getBaseOffset()+"\""+(openYear_ > 0 ? " opendate=\""+opendate+"\"" : "")+">"+corrIDs+"</line>\n";
  }

  public class CorridorInfo {

    int offsetFrom_, offsetTo_;
    int openMonth_, openYear_;

    public CorridorInfo(int offsetFrom, int offsetTo, int openMonth, int openYear) {
      offsetFrom_ = offsetFrom;
      offsetTo_ = offsetTo;
      openMonth_ = openMonth;
      openYear_ = openYear;
    }
  }
}
